package com.jenkins.test.proxy.gov.proxy;

import com.jenkins.test.proxy.gov.business.DefaultExecutor;
import com.jenkins.test.proxy.gov.business.GovEntranceInitialization;
import com.jenkins.test.proxy.gov.business.SigmoidSign;
import com.jenkins.test.proxy.gov.constant.GovStandardSigmoid;
import com.jenkins.test.proxy.gov.dto.GovRequest;
import com.jenkins.test.stream.ExpressCondition;
import com.jenkins.test.utils.ContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @version V1.0.0
 * @ClassName: {@link GovStandardServiceProxy}
 * @Description: GovStandardProxy
 * @author: Lanzhou
 * @date: 2022/1/22 14:01
 * @Copyright:2020 All rights reserved.
 */
@Slf4j
public class GovStandardServiceProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -7871633710923878862L;

    private GovEntranceInitialization entranceInitialization;

    private DefaultExecutor executor = new DefaultExecutor();

    private Map<GovStandardSigmoid, Method> methodCache = new HashMap<>();

    /**
     * 代理目标
     */
    private Object target;

    private Class<T> interfaceClass;

    /**
     * 绑定代理
     *
     * @return 代理对象
     */
    public Object bind(Class<T> interfaceClass) {
        this.interfaceClass = interfaceClass;
        Object instance = Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, this);
        this.target = instance;
        return instance;
    }

    /**
     * 执行
     *
     * @param proxy  代理
     * @param method 方法
     * @param args   参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        // 判断method是否有目标的类型没有的话直接跳过
        SigmoidSign sign = method.getAnnotation(SigmoidSign.class);
        if (sign != null) {
            // 第一个参数是自身, 所以我们不需要
            GovStandardSigmoid sigmoid = sign.value();
            GovRequest request = (GovRequest) args[0];
            // 初始化入口信息
            initializationEntrance(sigmoid, request);
            // 获取默认执行器
            return cachedInvoker(sigmoid).invoke(this.executor, sigmoid, request);
        }
        return method.invoke(target, args);
    }


    /**
     * 初始化入口
     *
     * @param govStandard 标准
     * @param request     请求
     */
    private void initializationEntrance(GovStandardSigmoid govStandard, GovRequest request) {
        ExpressCondition.runIf(entranceInitialization == null).carry(this::run);
        this.entranceInitialization.initialize(govStandard, request);
    }

    /**
     * SpringContext 中获取我们的Spring值
     */
    private void run() {
        this.entranceInitialization = ContextHolder.getBean(GovEntranceInitialization.class);
    }

    /**
     * 获取方法执行器
     *
     * @param sigmoid 执行器
     * @return
     */
    private Method cachedInvoker(GovStandardSigmoid sigmoid) {
        return methodCache.computeIfAbsent(sigmoid, mtd -> {
            try {
                Method method = this.executor.getClass().getMethod("execute", GovStandardSigmoid.class, GovRequest.class);
                // 懒得加载了, 直接缓存起来
                methodCache.put(sigmoid, method);
                return method;
            } catch (NoSuchMethodException e) {
                log.error("获取目标方法失败, 请稍后重试", e);
                return null;
            }
        });
    }

    public Class<T> getInterfaceClass() {
        return interfaceClass;
    }

    public void setInterfaceClass(Class<T> interfaceClass) {
        this.interfaceClass = interfaceClass;
    }

    public GovEntranceInitialization getEntranceInitialization() {
        return entranceInitialization;
    }

    public void setEntranceInitialization(GovEntranceInitialization entranceInitialization) {
        this.entranceInitialization = entranceInitialization;
    }

    public DefaultExecutor getExecutor() {
        return executor;
    }

    public void setExecutor(DefaultExecutor executor) {
        this.executor = executor;
    }

    public Map<GovStandardSigmoid, Method> getMethodCache() {
        return methodCache;
    }

    public void setMethodCache(Map<GovStandardSigmoid, Method> methodCache) {
        this.methodCache = methodCache;
    }

}
